package Market;

import java.util.ArrayList;

import edu.iastate.jrelm.core.SimpleAction;
import edu.iastate.jrelm.rl.rotherev.REPolicy;
import edu.iastate.jrelm.core.JReLMAgent;
import edu.iastate.jrelm.rl.ReinforcementLearner;
import edu.iastate.jrelm.rl.SimpleStatelessLearner;
import edu.iastate.jrelm.rl.rotherev.variant.VREParameters;


import sim.engine.*;


public class GenCo implements Steppable {
	private static final long serialVersionUID = 6628525522939315274L;
	
	
	  // GenCo's records by hours (within a day)
	private double[][] commitmentD;
	  private double[] commitment;  // Day-Ahead hourly power commitment quantity
	  private double[] dispatch;    // Real-Time hourly power dispatch quantity
	  private double[] dayAheadLMP; // Day-Ahead hourly locational marginal price
	  private double[][] dayAheadLMPD;
	  private double[] realTimeLMP; // Real-Time hourly locational marginal price
	  private double[] totalVariableCost;  // totalVariableCost = A*power + B*power^2
	  private double[] hourlyTotalCost;          // hourlyTotalCost = totalVariableCost + FCost

	  private double[] hourlyVariableCost;// hourlyVariableCost[h] = a*power + b*power^2
	  private double[] hourlyNetEarning;// hourlyNetEarning[h] = dispatch[h]*lmp[h]
	  private double[] hourlyProfit;// hourlyProfit[h] = dispatch[h]*lmp[h] - hourlyTotalCost[h]
	  private double  dailyNetEarnings;   // dailyNetEarnings = sum of hourlyNetEarning over 24 hours
	  private double dailyProfit;   // dailyProfit = sum of hourlyProfit over 24 hours
	  private double money;    // GenCo's accumulative money holding,
	                           // money (new) = money(previous) + dailyProfit(new)

	  private double[] hourlyRevenue;
	  private double dailyRevenue;  // 
	  private double dailyRevenueCE; // dailyRevenue under CE (Competitive Equil) case
	  private double marketAdvantage; // marketAdvantage = (dailyNetGain - dailyNetGainCE)/dailyNetGainCE
	  private double[] lernerIndex;  // lerner index (at dispatched level for each hour)
	  private double[] marketPower;  // market power measure (at dispatched level for each hour)

	  private double choiceProbability; // gen's learning choice probability
	  private double choicePropensity;  // gen's learning choice Propensity
	  private int    choiceID;  // gen's learning choice ID
	  
	  private static final int Hours = 24;
	  private double FixedCost;

	private double aR;
	private double bR;
	private double CapRL;
	private double CapRU;
	private double[] RSupplyOffer;   // Reported supply offer (aR,bR,CapRL,CapRU), strategic
	
	private double aT; 
	private double bT; 
	private double CapTU;
	private double CapTL;
	
	private double SS = 0.05; // slope start
	private double[] TSupplyOffer;  // True supply offer (a,b,CapTL,CapTU)

	private double l;
	private double u;
	private double lR;
	private double uR;
	private double us;
	
	 double RCapL;
	 double RCapU;
	 double RIL;
	 double RIU;
	 
	 double M1;
	 double M2;
	 double M3;
	 
	double RIMaxL;
	double RIMaxU;
	double RIMinC;
	 
	 double Propensity;
	 double Cooling;
	 double Recency;
	 double Experimentation;
	 
	 double RewardSelection;
	
	 ArrayList ADList; //action domain
	
	private double[] alpha;
	double M;
	
	private double[] LMP;
											/** number of GenCos */
	private int ii;
	
	Market market;
	boolean Switch;
		/** Array of Generators' locations {gen1 at node 1, gen2 at node3,.., geni at nodej} */
	  private int[] GenCoAtNodeA;   
	  private int GenCoAtNode;
	  private double[][] GenCoData;
	  int day;
	  private double[] GenCoMoneyD;
	  private double GenCoMoney;
	  Initials.InitGenCo initgenco = new Initials.InitGenCo(); //????????
	  
	  ArrayList LearnerList;
	
	  // JReLM component
	  private SimpleStatelessLearner Learner;
	  int RandomSeed;

//----Constructor------------------------------------------------------------------------------------	
	public GenCo(double[][] gencodata, double[][] learningdata, int i, int randomseed, Market mkt, int d, boolean sw ){
	
		market = mkt;
		ii = i;
		RandomSeed = randomseed;
		day = d;
		Switch = sw;

		
		
		FixedCost = gencodata[ii][2];
		aT     = gencodata[ii][3];
		bT     = gencodata[ii][4];
		CapTL  = gencodata[ii][5];
		CapTU  = gencodata[ii][6];
	
		
		Propensity  	= learningdata[ii][0];
		Cooling  		= learningdata[ii][1];
		Recency         = learningdata[ii][2];
		Experimentation = learningdata[ii][3];
		
		M1  			= learningdata[ii][4];
		M2  			= learningdata[ii][5];
		M3  			= learningdata[ii][6];
		RIMaxL   		= learningdata[ii][7];
		RIMaxU   		= learningdata[ii][8];
		RIMinC  		= learningdata[ii][9];
		SS   			= learningdata[ii][10];
		RewardSelection = learningdata[ii][11];
	}
	
	public GenCo(double[][] gencodata, int i, Market mkt, int d, boolean sw, ArrayList lrn){
		
		
		market = mkt;
		ii = i;
		day = d;
		Switch = sw;
		
		FixedCost = gencodata[ii][2];
		aT        = gencodata[ii][3];
		bT        = gencodata[ii][4];
		CapTL     = gencodata[ii][5];
		CapTU     = gencodata[ii][6];
	//	Learner = lrn;
		
	}
	

	//-----------------------------------------------------------------------------------------------------------------------------------------
    public void step(SimState state)
        {
    	 // ActionDomainConstruction();
    	 // ReportedSupplyOfferValidation(0.10, 0.20, 1.00);
    	
    	//Switch = market.getSwitch();
    	
    	if(!Switch){
    	
    	VREParameters learningParams = new VREParameters(Cooling, Experimentation, Propensity, Recency, RandomSeed);	    	
    	   	
    	ADList = ActionDomainConstruction();
    	SimpleStatelessLearner learner = new SimpleStatelessLearner(learningParams, ADList);
    	
    	//SimpleAction lastAction = (SimpleAction) learner.getPolicy().getLastAction();
    	
        double[] Triplet = (double[]) learner.chooseActionRaw();
        
        aR    = Triplet[0];
        bR    = Triplet[1];        
        CapRU = Triplet[2];
        CapRU = CapTU;
    	
    	
    	
    	market.addAactionDomain(ADList);
    	
    	System.out.println(" Set Learner from genco "); 
    	Learner = learner;
  
    	//market.setLearnerList(learner);
    	
    	
    	
    	market.addGenCo(this);
    	
    	SetRSupplyOffer();
    	setTSupplyOffer();
    	
    	market.setRSupplyOffer(RSupplyOffer, ii);
    	market.setTSupplyOffer(TSupplyOffer, ii);
    	
    	//System.out.println("switch is false  " +ii+ "   day= "+day);
    	}
    	
    	
    	
    	else{
    		

    		
	  	 //	Initials.InitGenCo initgenco = new Initials.InitGenCo(5); //????????
	  	 	GenCoAtNodeA = initgenco.getGenCoAtNode();
	  	 	GenCoAtNode = GenCoAtNodeA[ii];
	  	 	
	  	 	
	  	 	//System.out.println("switch is true  " +ii+ "  day= "+day);
    		
    			for(int j=1; j<6; j++){    				
    				if (GenCoAtNode == j){   					
    					updateSupplyOffer(j);
    					//System.out.println(ii);	
    		        } 
    			}
    		

    		
    	}
    	
    	
    	
    	//for (int k=0;k<4; k++){
    	//System.out.println(TSupplyOffer[k]);
    	//}
        }
    
//------------------------------------------------------------------------------------------------------------------------------------------    
/** The function checks whether reported supply offer is feasible or not 
 * @see <f/> DynTestAMES.JSLT.pdf (pg 34)  */    
    private void ReportedSupplyOfferValidation(double x, double y, double z) {
    	

    	
    	//RIL = x;
    	//RIU = y;
    	//RCapU = z;
    	
    	RCapL = CapTL/CapTU;
    	
//Step 0
   CapRL = CapTL;
   CapRL = RCapL*CapTU;
   CapRU = RCapU*(CapTU-CapTL)+CapTL;  

   l = aT+2*bT*CapTL;
   u = aT+2*bT*CapRU;

//Step 1
   lR = l/(1-RIL);
   
//Step2
   if(u > lR) { us = u; }
	   
     else {us = lR+SS;}
	  
//Step3
   uR = us/(1-RIU);
   
//Step4
   bR = 0.5*(uR-lR)/(CapRU-CapRL);
   
//Step5
   aR = lR-2*bR*CapTL;
   
   System.out.println("a_reported = " + aR);
   System.out.println("b_reported = " + bR);
    }
    
//------------------------------------------------------------------------------------------------------------------------------------------    

private ArrayList ActionDomainConstruction(){
	

    
	ArrayList ADMat = new ArrayList();
    
    M = M1 * M2 * M3;


	
	
	double Inc1 = RIMaxL/(M1 - 1);
	double Inc2 = RIMaxU/(M2 - 1);
	double Inc3 = (1-RIMinC)/(M3-1);
	
	for (double i = 0; i <= RIMaxL; i = i+Inc1){
		for (double j = 0; j <= RIMaxU; j = j+Inc2 ){
			for(double k = 1-RIMinC; k >= 0; k = k-Inc3){  
				
	            alpha = new double[3]; // initialize triplet to zeros
	            alpha[0] = i; 
	            alpha[1] = j; 
	            alpha[2] = k +RIMinC ; 
    
	            
	            ADMat.add(alpha);
	            
	           // System.out.println(alpha[0] + "   " + alpha[1] + "   " + alpha[2]);
	            
		
			}		
    	}
	}
	
	return ADMat;
}

public void SetRSupplyOffer(){

	RSupplyOffer = new double[4];
	
		RSupplyOffer[0] = aR;
		RSupplyOffer[1] = bR;
		RSupplyOffer[2] = CapRL;
		RSupplyOffer[3] = CapRU;
	
  }
    

public void setTSupplyOffer(){

	TSupplyOffer = new double[4];
	
	TSupplyOffer[0]=aT;
	TSupplyOffer[1]=bT;
	TSupplyOffer[2]=CapTL;
	TSupplyOffer[3]=CapTU;
	
	//System.out.println(TSupplyOffer[1]);
    
  }
	




public void updateProfit(int j){
	
	//Switch = market.getSwitch();
	commitmentD = market.getCommitment();
	commitment = new double[Hours];
	
	for (int h=0;h<Hours;h++){
		commitment[h]=commitmentD[h][ii];			
	}
	
	dayAheadLMPD = market.getLMP();
	dayAheadLMP = new double[Hours];
	for (int h=0;h<Hours;h++){
		dayAheadLMP[h]=dayAheadLMPD[h][j-1];			
	}
	
   // dailyProfit = new double[5];
   // dailyNetEarnings= new double[5];
   // dailyRevenue= new double[5];
    
    hourlyVariableCost = new double[24];
    hourlyTotalCost = new double[24];
    hourlyRevenue = new double[24];
    hourlyProfit = new double[24];
    hourlyNetEarning  = new double[24];
    
    dailyProfit = 0;
    dailyNetEarnings = 0;
    dailyRevenue = 0;

    for(int h=0; h<Hours; h++){
        hourlyVariableCost[h]  = aT * commitment[h] + bT * commitment[h]  * commitment[h] ;
        hourlyTotalCost[h]  = hourlyVariableCost[h]+ FixedCost;
        hourlyRevenue[h]  = commitment[h] *dayAheadLMP[h];
       
       // System.out.println("gen"+ii+" LSE= "+j+" hour"+h+" commitment="+commitment[h][ii]+"  dayAheadLMP=  "+dayAheadLMP[h][j]);
        
        hourlyProfit[h] = hourlyRevenue[h] - hourlyTotalCost[h];
        hourlyNetEarning[h] = hourlyRevenue[h] - hourlyVariableCost[h];
        
        dailyProfit += hourlyProfit[h];
        dailyNetEarnings += hourlyNetEarning[h];
        dailyRevenue += hourlyRevenue[h];
    }
   // System.out.println("day= "+day +" gen="+ii+" profit= "+dailyProfit[ii]);
 
}

	private void updateMoney(){
		
		if (day<1){
		GenCoMoneyD = initgenco.getGenCoMoney();
	    GenCoMoney = GenCoMoneyD[ii];
	
		
		GenCoMoney = GenCoMoney + dailyProfit;
		market.setGenCoMoney(ii, GenCoMoney);
		

		//System.out.println("day= "+day+" GenCo "+ii+" money holdings: "+ GenCoMoney[ii]);
		}
			else{				
				market.setGenCoMoney(ii, dailyProfit);
				
				//System.out.println(dailyProfit[ii]);
				//System.out.println("day= "+day+" GenCo "+ii+" money holdings: "+ GenCoMoney[ii]);
		}
		
	}
	  // genAgent learning (updating propensity based on current period dailyProfit)
/*	  private void learn(){
	    //System.out.println("Learning Report for GenCo: " + getID() );
	    SimpleAction lastAction = (SimpleAction) Learner.getPolicy().getLastAction();
	    choiceID = lastAction.getID();
	    double[] act = (double[]) lastAction.getAct();
	    REPolicy policy = (REPolicy)Learner.getPolicy();
	/*
	    System.out.printf("\tLast action chosen:  id= " + id +
	                       ";\t(lowerRI, upperRI, upperRCap)=(%1$6.4f, %2$6.4f, %3$6.4f)\n",
	                       act[0], act[1], act[2]);
	    
	    if(Double.isNaN(policy.getProbability(id))){
	        System.out.printf("\tBefore update --> the policy.getProbability return value is not a number!!!\n");

	        System.out.printf("\tBefore updating with daily profit: probability=%1$6.4f\tpropensity=%2$f\n",
	                         1.0,policy.getPropensity(id));
	    }
	    else
	        System.out.printf("\tBefore updating with daily profit: probability=%1$6.4f\tpropensity=%2$f\n",
	                         policy.getProbability(id),policy.getPropensity(id));
	 */
	  //  if(iRewardSelection==0)// profit
//	    Learner.update(new Double(dailyProfit));

	   // if(iRewardSelection==1)// net earnings
	   //     learner.update(new Double(dailyNetEarnings));
	/*
	    if(Double.isNaN(policy.getProbability(id))){
	        System.out.printf("\tAfter update --> the policy.getProbability return value is not a number!!!\n");
	        
	        System.out.printf("\tAfter updating with daily profit:  probability=%1$6.4f\tpropensity=%2$f\n\n",
	                         1.0,policy.getPropensity(id));
	    }
	    else
	        System.out.printf("\tAfter updating with daily profit:  probability=%1$6.4f\tpropensity=%2$f\n\n",
	                         policy.getProbability(id),policy.getPropensity(id));
	    
	*/
//	    choiceProbability = policy.getProbability(choiceID);
//	    choicePropensity=policy.getPropensity(choiceID);

//	  }
	  
	  public void updateSupplyOffer(int j){
		    updateProfit(j);

		   // if(flag != 1){
		      updateMoney();
		   // }

		   // learn();
		    
		   // iDayCount++;
		  /*  if(bActionProbabilityCheck&&(day>=0)){
		        updateActionProbabilities();
		    }
		    
		    iLearningCheckDayCount++;
		    if(bLearningCheck&&(iLearningCheckDayCount>=iLearningCheckStartDay)){
		        updateLearningResult();
		    }
		    
		    iDailyNetEarningDayCount++;
		    if(bDailyNetEarningThreshold&&(iDailyNetEarningDayCount>=iDailyNetEarningStartDay)){
		        updateDailyNetEarningResult();
		    } */
		  }
	  
	    public SimpleStatelessLearner getLearner(){
	    	System.out.println(" get Learner from genco "); 
	   	return Learner;
	   }
} 

   













